import os
import time
import math
import requests
import glob
import xml.etree.ElementTree as ET
from lxml import etree

def split_bbox_into_sub_regions(bbox, max_size=0.25):
    """
    将大的边界框划分为多个符合API限制的子区域
    
    参数:
    - bbox: 原始区域 [min_lon, min_lat, max_lon, max_lat]
    - max_size: API允许的最大宽度和高度（度）
    
    返回:
    - 子区域列表
    """
    min_lon, min_lat, max_lon, max_lat = bbox
    width = max_lon - min_lon
    height = max_lat - min_lat
    
    # 计算需要划分的格子数
    cols = math.ceil(width / max_size)
    rows = math.ceil(height / max_size)
    
    sub_regions = []
    
    for row in range(rows):
        for col in range(cols):
            # 计算子区域的边界
            sub_min_lon = min_lon + col * (width / cols)
            sub_max_lon = min_lon + (col + 1) * (width / cols)
            sub_min_lat = min_lat + row * (height / rows)
            sub_max_lat = min_lat + (row + 1) * (height / rows)
            
            # 确保边界不超过原始区域
            sub_max_lon = min(sub_max_lon, max_lon)
            sub_max_lat = min(sub_max_lat, max_lat)
            
            sub_regions.append([sub_min_lon, sub_min_lat, sub_max_lon, sub_max_lat])
    
    return sub_regions

def count_track_points(gpx_content):
    """
    计算GPX内容中的轨迹点数量
    """
    try:
        # 将内容解析为XML
        root = ET.fromstring(gpx_content)
        
        # 在OpenStreetMap的GPX中，命名空间为默认命名空间
        # 因此需要使用{namespace}trkpt来查找元素
        ns = {'gpx': 'http://www.topografix.com/GPX/1/0'}
        
        # 查找所有的trkpt元素
        track_points = root.findall('.//gpx:trkpt', ns)
        return len(track_points)
    except Exception as e:
        print(f"解析GPX内容时出错: {e}")
        # 如果解析失败，返回内容长度作为备选指标
        return len(gpx_content)

def fetch_and_save_gpx_files(bbox, output_folder="gpx_files", max_page_size=5000, 
                            retry_limit=3, retry_delay=5):
    """
    获取指定区域的GPX轨迹点数据并保存
    
    参数:
    - bbox: 边界框 [min_lon, min_lat, max_lon, max_lat]
    - output_folder: 输出文件夹
    - max_page_size: 每页最大结果数
    - retry_limit: 重试次数限制
    - retry_delay: 重试延迟(秒)
    """
    # 检查边界框尺寸
    min_lon, min_lat, max_lon, max_lat = bbox
    width = max_lon - min_lon
    height = max_lat - min_lat
    
    # 如果区域超过API限制，划分为子区域
    if width > 0.25 or height > 0.25:
        print(f"区域大小({width}x{height})超过API限制(0.25x0.25)，正在划分为子区域...")
        sub_regions = split_bbox_into_sub_regions(bbox)
        print(f"已将区域划分为{len(sub_regions)}个子区域")
        
        for i, sub_bbox in enumerate(sub_regions):
            print(f"正在处理子区域 {i+1}/{len(sub_regions)}: {sub_bbox}")
            # 为每个子区域创建子文件夹
            sub_folder = f"{output_folder}/region_{i+1}"
            fetch_and_save_gpx_files(sub_bbox, sub_folder, max_page_size, retry_limit, retry_delay)
        
        return
    
    # 创建输出文件夹
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    
    # 设置请求头部
    headers = {
        'User-Agent': 'GPX_Downloader/1.0 (https://example.com; your_email@example.com)'
    }
    
    page = 0
    while True:
        # 构造 API 请求 URL
        url = f'https://api.openstreetmap.org/api/0.6/trackpoints?bbox={min_lon},{min_lat},{max_lon},{max_lat}&page={page}'
        
        # 发送请求，带重试机制
        success = False
        response = None
        
        for attempt in range(retry_limit):
            try:
                print(f"正在获取页面 {page}...")
                response = requests.get(url, headers=headers, timeout=30)
                
                # 处理可能的限流情况
                if response.status_code == 429:
                    wait_time = int(response.headers.get('Retry-After', retry_delay * 2))
                    print(f"API限流，等待{wait_time}秒后重试...")
                    time.sleep(wait_time)
                    continue
                    
                # 如果请求成功，标记并跳出重试循环
                if response.status_code == 200:
                    success = True
                    break
                    
                # 其他错误情况
                print(f"请求失败，状态码: {response.status_code}，尝试重试({attempt+1}/{retry_limit})...")
                time.sleep(retry_delay)
                
            except requests.exceptions.RequestException as e:
                print(f"请求异常: {e}，尝试重试({attempt+1}/{retry_limit})...")
                time.sleep(retry_delay)
        
        # 如果所有重试都失败
        if not success:
            print(f"获取页面 {page} 失败，放弃此区域的下载")
            break
        
        # 保存返回的 GPX 文件到本地
        gpx_filename = f'{output_folder}/page_{page}.gpx'
        with open(gpx_filename, 'wb') as file:
            file.write(response.content)
        
        print(f"已保存 page {page} 的 GPX 文件：{gpx_filename}")
        
        # 计算实际轨迹点数量
        track_point_count = count_track_points(response.content)
        print(f"该页面包含 {track_point_count} 个轨迹点")
        
        # 如果返回的数据点少于 max_page_size，表示已没有更多页面
        if track_point_count < max_page_size:
            print("已达到最后一页")
            break
        
        # 限流保护：每次请求之间等待一段时间
        time.sleep(1)
        
        # 递增页码获取下一页
        page += 1

def remove_namespace(element):
    """递归移除XML元素的命名空间"""
    if isinstance(element.tag, str) and element.tag.startswith("{"):
        # 移除命名空间部分s
        element.tag = element.tag.split("}")[1]
    
    # 递归处理所有子元素
    for child in element:
        remove_namespace(child)

# 合并GPX文件的函数
def merge_gpx_files(input_directory, output_file):
    with open(output_file, 'wb') as f:
        # 创建根元素cl
        root = etree.Element("gpx", version="1.0", creator="OpenStreetMap.org",xmlns="http://www.topografix.com/GPX/1/0")
        gpx_files = glob.glob(os.path.join(input_directory, '**', '*.gpx'), recursive=True)
        # 遍历目录下的所有GPX文件
        for gpx_file in gpx_files:
                
            # 解析GPX文件
            tree = etree.parse(gpx_file)
            gpx_root = tree.getroot()
            remove_namespace(gpx_root)
            # 提取文件中的所有trk元素
            for trk in gpx_root.findall("trk"):
                # 将每个trk元素添加到根元素中
                root.append(trk)

            f.write(etree.tostring(root, pretty_print=True, xml_declaration=True, encoding="UTF-8"))
    print('合并成功')
# 使用示例
if __name__ == "__main__":
    # 设置区域边界框（bbox）[min_lon, min_lat, max_lon, max_lat]
    # 这是一个较大的区域，会被自动划分
    # bbox = [119.924, 30.1226, 120.5502, 30.4416]  # 杭州
    
    # # 下载文件
    # download_gpx_for_area(bbox, "hangzhou_gpx")
    input_folder = 'hangzhou_gpx'
    output_file = './all_page.gpx'
        
    # 如果需要一个文件可以合并
    merge_gpx_files(input_folder, output_file)